package tinyinventor.compiler;

import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import codeblocks.Block;
import codeblocks.BlockConnector;
import codeblocks.BlockGenus;
import codeblocks.BlockLinkChecker;
import codeblocks.BlockStub;

/**
 * Each Block is represented by a component during the compile process.
 * A component contains information about how a block is to be compiled.
 * 
 * @author Morten Tranberg Hansen
 *
 */

public abstract class Component {

	private String name;
	
	private Long id;
	private String type;
	protected String label;
	private List<String> argCode;	
	
	public Component(String componentName) {
		name = componentName;
		id = Block.NULL;
		type = "";
		label = "";
		argCode = new LinkedList<String>();
	}
	
	public String getName() {
		return name;
	}
	
	public abstract String getCode();
	
	/**
	 * Compile the given block and return the corresponding code. 
	 * @param block The block to be compiled
	 * @return block code
	 * @throws CompileException if a child Block's corresponding Component cannot be found
	 */
	public String compile(Compiler compiler, Block block) throws CompileException {
		BlockGenus genus = BlockGenus.getGenusWithName(block.getGenusName());
		System.out.println("compileBlock: " + name + ", blockName: " + block.getGenusName());
		// Set id
		id = block.getBlockID();
		// Set type
		if(genus.isDataBlock() && block.getPlugKind()!=null) {
			type = Compiler.cleanString(block.getPlugKind());
		} else if(genus.isCommandBlock() && block.getNumSockets()>0) {
			type = Compiler.cleanString(block.getSocketAt(0).getKind());
		} else if(genus.isVariableDeclBlock() && block.getNumSockets()>0) {
			type = Compiler.cleanString(block.getSocketAt(0).getKind());
		}
		// Set label
		label = block.getBlockLabel();
		String prefix = block.getLabelPrefix();
		String suffix = block.getLabelSuffix();
		if(label.regionMatches(0, prefix, 0, prefix.length())) {
			label = label.substring(prefix.length());
		}
		if(label.regionMatches(label.length()-suffix.length(), suffix, 0, suffix.length())) {
			label = label.substring(0, label.length()-suffix.length());
		}
		// Get argument codes
		for(BlockConnector conn : BlockLinkChecker.getSocketEquivalents(block)) {
			if(conn.hasBlock()) {
				Block childBlock = Block.getBlock(conn.getBlockID());
				System.out.println(name + " has connector: " + childBlock.getGenusName());
				if(childBlock.isLabelValue()) {
					argCode.add(childBlock.getBlockLabel());
				} else {
					Component childComponent = compiler.getComponent(childBlock);
					argCode.add(childComponent.compile(compiler, childBlock));
				}
			} else {
				argCode.add("");
			}
		}
		// Add checks
		if(block instanceof BlockStub) {
			compiler.addCheck(new ParentCheck(block));
			compiler.addCheck(new ConnectorCheck(block));
		}
		//System.out.println("returning from " + block.getGenusName() + ": " + substituteVars(rawCode));
		return substituteVars(getCode());
	}
		
	/**
	 * Substitute variables in code with current component state
	 * @param code the code to be processed
	 * @return the processed code
	 */
	protected String substituteVars(String code) {
		String result = new String(code);
		result = result.replace("##LABEL##", Compiler.cleanString(label));
		result = result.replace("##HASHLABEL##", (""+label.hashCode()).replaceAll("-", ""));
		result = result.replace("##VALUE##", label);
		result = result.replace("##ID##", String.valueOf(id));
		result = result.replace("##TYPE##", type);
		for(int i=0; i<argCode.size(); i++) {
			result = result.replace("##ARG"+(i+1)+"##", argCode.get(i));
		}
		if(argCode.size()>0) { 
			result = result.replace("##EXPANDABLE_NUM##", String.valueOf(argCode.size()-1));
			StringBuilder expandableArgs = new StringBuilder();
			for(int i=0; i<argCode.size()-1; i++) {
				if(expandableArgs.length()!=0) {
					expandableArgs.append(",");
				} 
				expandableArgs.append(argCode.get(i));
			}		
			result = result.replace("##EXPANDABLE_ARGS##", expandableArgs.toString());
		}
		return result;
	}

	public abstract Component clone();
	
}
